﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace SGUI
{
    public class ASTTree 
    {
        public List<ASTNode> Roots = new List<ASTNode>();
        int peekCount;
        ASTToken tok;
        lexer lex;
        public static ASTTree ParseString(string fileName, string scriptText)
        {
            ASTTree tree = new ASTTree();
            tree.Parse(fileName, scriptText);
            return tree;
        }
        public void Parse(string fileName, string scriptText)
        {
            //解析出所有token
            this.lex = new lexer(scriptText);
            this.lex.Run();
            while (true) 
            {
                ASTNode node = this.parse();
                if (node == null) 
                {
                    break;
                }
                this.Roots.Add(node);
            }
        }

        public ASTNode parse() 
        {
           
            ASTToken tok = this.next();
            if (tok.Typ == ASTToken.TokenType.tokSymbol) 
            {
                if (tok.Val == "property") 
                {
                    return this.parseProperty(tok);
                }
                else if (this.lex.nextTokenOnlyRead().Typ == ASTToken.TokenType.tokLeftBrace)
                {
                    return this.parseComponent(tok);
                }
                else if (this.lex.nextTokenOnlyRead().Typ == ASTToken.TokenType.tokColon)
                {
                    return this.parseAttrNode(tok);
                }
                else 
                {
                    SymbolNode symNode = new SymbolNode();
                    symNode.Val = tok.Val;
                    return symNode;
                }
            }
            else if (tok.Typ == ASTToken.TokenType.tokString) 
            {
                StringNode strNode = new StringNode();
                strNode.Position = tok.Pos;
                strNode.Val = tok.Val.Substring(1, tok.Val.Length - 2);
                return strNode;
            }
            else if (tok.Typ == ASTToken.TokenType.tokNumber) 
            {
                return this.parseNumber(tok);
            }
            else if (tok.Typ == ASTToken.TokenType.tokEOF)
            {
                return null;
            }
            else if (tok.Typ == ASTToken.TokenType.tokLineEnd) 
            {
                return null;
            }
            else
            {
                this.unexpected(tok);
                return null;
            }

            return null;
        }

        public ASTNode parseProperty(ASTToken start) 
        {
            DefinePropertyNode propertyNode = new DefinePropertyNode();
            ASTToken propertyType =  this.next();
            switch (propertyType.Val) 
            {
                case "string":
                    propertyNode.propertyType = DefinePropertyNode.PropertyType.String;
                    propertyNode.DefaultValue = "";
                break;
                case "int":
                    propertyNode.propertyType = DefinePropertyNode.PropertyType.Int;
                    propertyNode.DefaultValue = 0;
                break;
                case "float":
                    propertyNode.propertyType = DefinePropertyNode.PropertyType.Float;
                    propertyNode.DefaultValue = 0d;
                break;
            }
            ASTToken propertyName = this.next();
            propertyNode.PropertyName = propertyName.Val;
            ASTToken nextTok = this.next();
            if (nextTok.Typ == ASTToken.TokenType.tokEqual)
            {
                nextTok = this.next();
                switch(propertyNode.propertyType)
                {
                    case DefinePropertyNode.PropertyType.String:
                        propertyNode.DefaultValue = nextTok.Val.Substring(1,nextTok.Val.Length-2);
                    break;
                    case DefinePropertyNode.PropertyType.Int:
                        propertyNode.DefaultValue =  int.Parse(nextTok.Val);
                    break;
                    case DefinePropertyNode.PropertyType.Float:
                        propertyNode.DefaultValue = double.Parse(nextTok.Val);
                    break;
                }
                this.next();
            }
            return propertyNode;
        }
        public ASTNode parseComponent(ASTToken start)
        {
            List<ASTNode> nodes = new List<ASTNode>();
            while (true)
            {
                ASTToken tok = this.next();
                switch (tok.Typ)
                {
                    //}
                    case ASTToken.TokenType.tokRightBrace:
                        ComponentNode comNode = new ComponentNode();
                        comNode.ComponentName = start.Val;
                        comNode.nodes = nodes;
                        return comNode;
                    case ASTToken.TokenType.tokEOF:
                        this.unexpectedEOF(tok);
                      break;
                }
                if (tok.Typ != ASTToken.TokenType.tokLeftBrace)
                {
                    this.backup();
                }
                ASTNode node = this.parse();
                if (ASTNode.isSemantic(node)) 
                {
                    nodes.Add(node);
                }
            }
         
        }

        public ASTNode parseAttrNode(ASTToken start) 
        {
            AttributeNode attrNode = new AttributeNode();
            attrNode.AttrName = start.Val;
            this.next();
            ASTNode valueNode = this.parse();
            attrNode.ValueNode = valueNode;
            this.next();
            return attrNode;
        }

        public void backup()
        {
            this.peekCount++;
            if (this.peekCount > 1)
            {
                Debug.LogError("backup() called twice consecutively");
            }
        }
        public ASTToken next()
        {
            if (this.peekCount > 0)
            {
                this.peekCount--;
            }
            else
            {
                this.tok = nextToken();
            }
            return this.tok;
        }
        public ASTToken nextToken()
        {
            ASTToken tok = this.lex.nextToken();

            if (tok.Typ == ASTToken.TokenType.tokError)
            {
                Debug.LogError(tok.ToString());
            }
            return tok;
        }

        public void unexpected(ASTToken token)
        {
            this.errorf(tok.Pos, "unexpected token " + tok.Val);
        }
        public void unexpectedEOF(ASTToken tok)
        {
            this.errorf(tok.Pos, "unexpected token " + tok.Val);
        }
        public void errorf(ASTPos pos, string str)
        {
            Debug.LogError("parseError");
        } 
        public ASTNode parseNumber(ASTToken tok)
        {
            NumberNode numberNode = new NumberNode();
            numberNode.Position = tok.Pos;
            string numberExt = tok.Val.Substring(tok.Val.Length-1,1);

            if (numberExt == "f")
            {
                numberNode.Type = NumberNode.NumberType.DOUBLE;
                numberNode.NumberVal = (double)float.Parse( tok.Val.Substring(0, tok.Val.Length - 1));
            }
            else 
            {
                numberNode.Type = NumberNode.NumberType.INT;
                numberNode.NumberVal = int.Parse(tok.Val);
            }
            return numberNode;
        }

    }
}
